﻿using System;
using System.ComponentModel;
using System.Threading;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
using Timer = System.Timers.Timer;

namespace LogoFX.Controls
{
    #region LineStackingStrategyBoxes

    public static class LineStackingStrategyBoxes
    {
        public static readonly object BlockLineHeightBox = LineStackingStrategy.BlockLineHeight;
        public static readonly object MaxHeightBox = LineStackingStrategy.MaxHeight;

        public static object Box(LineStackingStrategy value)
        {
            switch (value)
            {
                case LineStackingStrategy.BlockLineHeight: return BlockLineHeightBox;
                case LineStackingStrategy.MaxHeight: return MaxHeightBox;
                default: return value;
            }
        }
    }

    #endregion

    #region TextAlignmentBoxes

    public static class TextAlignmentBoxes
    {
        public static readonly object LeftBox = TextAlignment.Left;
        public static readonly object RightBox = TextAlignment.Right;
        public static readonly object CenterBox = TextAlignment.Center;
        public static readonly object JustifyBox = TextAlignment.Justify;

        public static object Box(TextAlignment value)
        {
            switch (value)
            {
                case TextAlignment.Left: return LeftBox;
                case TextAlignment.Right: return RightBox;
                case TextAlignment.Center: return CenterBox;
                case TextAlignment.Justify: return JustifyBox;
                default: return value;
            }
        }
    }

    #endregion

    #region TextTrimmingBoxes

    public static class TextTrimmingBoxes
    {
        public static readonly object NoneBox = TextTrimming.None;
        public static readonly object CharacterEllipsisBox = TextTrimming.CharacterEllipsis;
        public static readonly object WordEllipsisBox = TextTrimming.WordEllipsis;

        public static object Box(TextTrimming value)
        {
            switch (value)
            {
                case TextTrimming.None: return NoneBox;
                case TextTrimming.CharacterEllipsis: return CharacterEllipsisBox;
                case TextTrimming.WordEllipsis: return WordEllipsisBox;
                default: return value;
            }
        }
    }

    #endregion

    #region TextWrappingBoxes

    public static class TextWrappingBoxes
    {
        public static readonly object WrapWithOverflowBox = TextWrapping.WrapWithOverflow;
        public static readonly object NoWrapBox = TextWrapping.NoWrap;
        public static readonly object WrapBox = TextWrapping.Wrap;

        public static object Box(TextWrapping value)
        {
            switch (value)
            {
                case TextWrapping.WrapWithOverflow: return WrapWithOverflowBox;
                case TextWrapping.NoWrap: return NoWrapBox;
                case TextWrapping.Wrap: return WrapBox;
                default: return value;
            }
        }
    }

    #endregion

    [TemplatePart(Name = "PART_TextBlock", Type = typeof(TextBlock))]
    [TemplatePart(Name = "PART_TextBox", Type = typeof(TextBox))]
    public class EditableTextBlock : Control
    {
        #region Dependency Properties

        #region IsEditMode Dependency Property

        public static readonly DependencyProperty IsEditModeProperty = DependencyProperty.Register(
#if SILVERLIGHT
            "IsEditMode", typeof(bool), typeof(EditableTextBlock),new PropertyMetadata(OnIsEditModeChanged));
#else
            "IsEditMode", typeof(bool), typeof(EditableTextBlock),new FrameworkPropertyMetadata(false,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsEditModeChanged));
#endif
        private static void OnIsEditModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            
        }

        public bool IsEditMode
        {
            get { return (bool)GetValue(IsEditModeProperty); }
            set { SetValue(IsEditModeProperty, value); }
        }

        #endregion

        #region IsHyphenationEnabled Dependency Property

        public static readonly DependencyProperty IsHyphenationEnabledProperty =
            Block.IsHyphenationEnabledProperty.AddOwner(typeof(EditableTextBlock));

        public bool IsHyphenationEnabled
        {
            get { return (bool)base.GetValue(IsHyphenationEnabledProperty); }
            set { base.SetValue(IsHyphenationEnabledProperty, value); }
        }

        #endregion

        #region LineHeight Dependency Property

        public static readonly DependencyProperty LineHeightProperty =
            Block.LineHeightProperty.AddOwner(typeof(EditableTextBlock));

        [TypeConverter(typeof(LengthConverter))]
        public double LineHeight
        {
            get { return (double)base.GetValue(LineHeightProperty); }
            set { base.SetValue(LineHeightProperty, value); }
        }

        #endregion

        #region LineStackingStrategy Dependency Property

        public static readonly DependencyProperty LineStackingStrategyProperty =
            Block.LineStackingStrategyProperty.AddOwner(typeof(EditableTextBlock));

        public LineStackingStrategy LineStackingStrategy
        {
            get { return (LineStackingStrategy)base.GetValue(LineStackingStrategyProperty); }
            set { base.SetValue(LineStackingStrategyProperty, LineStackingStrategyBoxes.Box(value)); }
        }

        #endregion

        #region TextAlignment Dependency Property

        public static readonly DependencyProperty TextAlignmentProperty =
            Block.TextAlignmentProperty.AddOwner(typeof(EditableTextBlock));

        public TextAlignment TextAlignment
        {
            get { return (TextAlignment)base.GetValue(TextAlignmentProperty); }
            set { base.SetValue(TextAlignmentProperty, TextAlignmentBoxes.Box(value)); }
        }

        #endregion

        #region Text Dependency Property

        public static readonly DependencyProperty TextProperty =
#if !SILVERLIGHT
            TextBox.TextProperty.AddOwner(typeof(EditableTextBlock),new FrameworkPropertyMetadata(null,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
#else
              TextBox.TextProperty.AddOwner(typeof(EditableTextBlock));
#endif

        public string Text
        {
            get { return (string)base.GetValue(TextProperty); }
            set { base.SetValue(TextProperty, value); }
        }

        #endregion

        #region TextTrimming Dependency Property

        public static readonly DependencyProperty TextTrimmingProperty =
            TextBlock.TextTrimmingProperty.AddOwner(typeof(EditableTextBlock));

        public TextTrimming TextTrimming
        {
            get { return (TextTrimming)base.GetValue(TextTrimmingProperty); }
            set { base.SetValue(TextTrimmingProperty, TextTrimmingBoxes.Box(value)); }
        }

        #endregion

        #region TextWrapping Dependency Property

        public static readonly DependencyProperty TextWrappingProperty =
            TextBlock.TextWrappingProperty.AddOwner(typeof(EditableTextBlock));

        public TextWrapping TextWrapping
        {
            get { return (TextWrapping)base.GetValue(TextWrappingProperty); }
            set { base.SetValue(TextWrappingProperty, TextWrappingBoxes.Box(value)); }
        }

        #endregion

        #region IsReadOnly Dependency Property

        public static readonly DependencyProperty IsReadOnlyProperty =
            TextBox.IsReadOnlyProperty.AddOwner(typeof(EditableTextBlock));

        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }

        #endregion

        #region ShouldAcceptValueOnLostFocus Dependency Property

        public static readonly DependencyProperty ShouldAcceptValueOnLostFocusProperty = DependencyProperty.Register(
            "ShouldAcceptValueOnLostFocus", typeof(bool), typeof(EditableTextBlock), new PropertyMetadata(false));


        public bool ShouldAcceptValueOnLostFocus
        {
            get { return (bool)GetValue(ShouldAcceptValueOnLostFocusProperty); }
            set { SetValue(ShouldAcceptValueOnLostFocusProperty, value); }
        }

        #endregion

        #endregion

        private TextBlock m_textBlock;
        private TextBox m_textBox;

        static EditableTextBlock()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(EditableTextBlock),
                                                     new FrameworkPropertyMetadata(typeof(EditableTextBlock)));
            //EventManager.RegisterClassHandler(typeof(EditableTextBlock), Mouse.LostMouseCaptureEvent, new MouseEventHandler(MouseOnLostMouseCapture));
            EventManager.RegisterClassHandler(typeof(EditableTextBlock), Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThrough));
            //EventManager.RegisterClassHandler(typeof(EditableTextBlock), Mouse.PreviewMouseUpOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThrough));

        }

        private static void OnClickThrough(object sender, MouseButtonEventArgs e)
        {
            if (Mouse.Captured == ((EditableTextBlock)sender).m_textBox)
            {
                if (((EditableTextBlock)sender).ShouldAcceptValueOnLostFocus)
                {
                    ((EditableTextBlock)sender).Text = ((EditableTextBlock)sender).m_textBox.Text;
                }
                ((EditableTextBlock)sender).IsEditMode = false;
                ((EditableTextBlock)sender).ReleaseMouseCapture();
            }
        }




        public TimeSpan EditDelay
        {
            get { return (TimeSpan)GetValue(EditDelayProperty); }
            set { SetValue(EditDelayProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EditDelay.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EditDelayProperty =
            DependencyProperty.Register("EditDelay", typeof(TimeSpan), typeof(EditableTextBlock), new UIPropertyMetadata(TimeSpan.FromSeconds(5)));



        public bool EnableClickEdit
        {
            get { return (bool)GetValue(EnableClickEditProperty); }
            set { SetValue(EnableClickEditProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EnableClickEdit.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EnableClickEditProperty =
            DependencyProperty.Register("EnableClickEdit", typeof(bool), typeof(EditableTextBlock), new UIPropertyMetadata(true));



        public LineBreakCondition BreakAfter
        {
            get { return LineBreakCondition.BreakDesired; }
        }

        public LineBreakCondition BreakBefore
        {
            get { return LineBreakCondition.BreakDesired; }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            m_textBox = (TextBox)GetTemplateChild("PART_TextBox");
            m_textBlock = (TextBlock)GetTemplateChild("PART_TextBlock");

            if (m_textBox == null)
            {
                throw new InvalidOperationException("Missing PART_TextBox!");
            }
            else if (m_textBlock == null)
            {
                throw new InvalidOperationException("Missing PART_TextBlock!");
            }

            Binding bindIsReadOnly = new Binding();
            bindIsReadOnly.Source = this;
            bindIsReadOnly.Path = new PropertyPath(IsReadOnlyProperty);
            m_textBox.SetBinding(TextBox.IsReadOnlyProperty, bindIsReadOnly);

            m_textBox.KeyDown += m_editBox_KeyDown;
            //m_textBox.LostFocus += m_textBox_LostFocus;
            m_textBox.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(m_textBox_LostKeyboardFocus);
            //m_textBox.LostMouseCapture += new MouseEventHandler(m_textBox_LostMouseCapture);
            m_textBox.IsVisibleChanged += new DependencyPropertyChangedEventHandler(m_textBox_IsVisibleChanged);
            //sanity check
            if (IsEditMode)
            {
                m_textBox.Text = Text;
                m_textBox.Focus();
                //Mouse.Capture(this, CaptureMode.SubTree);
                m_textBox.SelectAll();
            }
        }

        private void m_textBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            TextBox etb = (TextBox)sender;
            if (e.NewValue != null && (bool)e.NewValue)
            {
                etb.Text = Text;
                etb.Focus();
                //Mouse.Capture(this, CaptureMode.SubTree);
                etb.SelectAll();
            }
        }
        private void m_textBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (Keyboard.FocusedElement == null ||
                !(Keyboard.FocusedElement is Visual || Keyboard.FocusedElement is Visual3D) ||
                !IsAncestorOf(Keyboard.FocusedElement as DependencyObject))
            {
                IsEditMode = false;
                if (ShouldAcceptValueOnLostFocus)
                {
                    Text = m_textBox.Text;
                }
            }
        }
        //private void m_textBox_LostFocus(object sender, RoutedEventArgs e)
        //{
        //    if (Keyboard.FocusedElement == null ||
        //        !(Keyboard.FocusedElement is DependencyObject) ||
        //        !IsAncestorOf(Keyboard.FocusedElement as DependencyObject))
        //        IsEditMode = false;
        //}
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (EnableClickEdit && !IsEditMode)
            {
                Timer t = new Timer(EditDelay.TotalMilliseconds);
                t.Elapsed += Elapsed;
                t.AutoReset = false;
                t.Start();
            }
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.Key == Key.F2)
            {
                IsEditMode = true;
                e.Handled = true;
            }
        }
        private void Elapsed(object sender, ElapsedEventArgs e)
        {
            
            if (IsMouseOver)
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                    new ThreadStart(delegate
                    {
                        IsEditMode = true;
                    }));
            }
            ((Timer)sender).Dispose();
        }
        private void m_editBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                Text = m_textBox.Text;
                IsEditMode = false;
                ReleaseMouseCapture();
                e.Handled = true;
            }
            else if (e.Key == Key.Escape)
            {
                IsEditMode = false;
                ReleaseMouseCapture();
                e.Handled = true;
            }

        }
    }
}